package com.lpf.nashorn;

import com.lpf.common.Employee;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import org.junit.Before;
import org.junit.Test;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;

public class TestNashorn {

    private String filePath;

    @Before
    public void findFile() {
        URL resource = TestNashorn.class.getClassLoader().getResource("script.js");
        filePath = resource.getPath();
    }

	// ====================使用 Nashorn==================================================================================

	@Test
    public void test1() throws ScriptException {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.eval("print('Hello World!');");
    }

    @Test
    public void test2() throws ScriptException, FileNotFoundException {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.eval(new FileReader(filePath));
    }

    // ====================Java 调用 Javascript 函数=====================================================================

	/**
	 *  Nashorn 支持 java 代码直接调用定义在脚本文件中的 JavaScript 函数。
	 */
	@Test
    public void test_js_fun1() throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.eval(new FileReader(filePath));

        Invocable invocable = (Invocable) engine;

        Object result = invocable.invokeFunction("fun1", "Peter Parker");
        System.out.println(result);
        System.out.println(result.getClass());

        // 输出：
        // Hi there from Javascript, Peter Parker
        // greetings from javascript
        // class java.lang.String
    }

	/**
	 * 通过传递任意的 Java 对象去调用第二个函数
	 */
	@Test
    public void test_js_fun2() throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.eval(new FileReader(filePath));

        Invocable invocable = (Invocable) engine;

        invocable.invokeFunction("fun2", new Date());
        // [object java.util.Date]

        invocable.invokeFunction("fun2", LocalDateTime.now());
        // [object java.time.LocalDateTime]

        invocable.invokeFunction("fun2", new Employee());
        // [object com.lpf.common.Employee]
    }


	// ====================JavaScript 调用 Java 方法=====================================================================

	/**
	 * 在 JavaScript 中调用 Java 方法很简单。首先我们定义一个静态的 Java 方法：
	 *
	 * JavaScript 可通过 Java.type API 来引用 Java 类。这跟在 Java 类中引入其他类是类似的。
	 * 当定义了 Java 类型后我们可直接调用其静态方法 fun1() 并打印结果到 sout。因为方法是静态的，
	 * 所以我们无需创建类实例。
	 */
    public static String funJava1(String name) {
        System.out.format("Hi there from Java, %s", name);
        return " greetings from java";
    }

    @Test
    public void test_funJava1() throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.eval(new FileReader(filePath));
        Invocable invocable = (Invocable) engine;
        invocable.invokeFunction("funJava1");
    }


    /**
     * 原生 JavaScript 类型与 java 类型转换
     *
     * 原始的 Javascript 类型被转换为适当的 java 包装器类。而不是本地 Javascript 对象内部适配器类。
     * 请记住，这些类来自于jdk.nashorn.internal，所以你不应该在客户端使用这些类。
     */
    public static void funJava2(Object object) {
        System.out.println(object.getClass());
    }

    @Test
    public void test_funJava2() throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.eval(new FileReader(filePath));
        Invocable invocable = (Invocable) engine;
        invocable.invokeFunction("funJava2");
    }


    /**
     * 当使用 ScriptObjectMirror 把本地 JavaScript 对象传入时，实际上是有一个 java 对象表示 JavaScript 对象。
     * ScriptObjectMirror 实现了接口与jdk.nashorn.api 内部的映射。这个包下的类目的就是用于客户端代码使用。
     *
     * 更改参数类型 Object 为 ScriptObjectMirror，因此我们能获取到传入 JavaScript 中对象的一些信息。
     */
    public static void funJava3(ScriptObjectMirror mirror) {
        System.out.println(mirror.getClassName() + ": " + Arrays.toString(mirror.getOwnKeys(true)));
    }

    @Test
    public void test_funJava3() throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.eval(new FileReader(filePath));
        Invocable invocable = (Invocable) engine;
        invocable.invokeFunction("funJava3");
    }


    /**
     * 也可以在 Java 端调用 JavaScript 对象中的函数。
     * 首先定义一个JavaScript 类型 Person，包含属性 firstName、lastName 和函数 getFullName。
     */
    public static void funJava4(ScriptObjectMirror person) {
        System.out.println("Full Name is: " + person.callMember("getFullName"));
    }

    @Test
    public void test_funJava4() throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        engine.eval(new FileReader(filePath));
        Invocable invocable = (Invocable) engine;
        invocable.invokeFunction("funJava4");
    }
}
